<template>
  <div ref="rootEl">
    <GlassCard>
      <div class="display" id="basicDisplay">{{ displayText }}</div>
      <div class="grid cols-4 mt-[8px]">
        <button class="key" @click="onKey('C')" data-k="C">C</button>
        <button class="key" @click="onKey('BS')" data-k="BS">⌫</button>
        <button class="key" @click="onKey('%')" data-k="%">%</button>
        <button class="key" @click="onKey('/')" data-k="/">÷</button>

        <button class="key" @click="onKey('7')" data-k="7">7</button>
        <button class="key" @click="onKey('8')" data-k="8">8</button>
        <button class="key" @click="onKey('9')" data-k="9">9</button>
        <button class="key" @click="onKey('*')" data-k="*">×</button>

        <button class="key" @click="onKey('4')" data-k="4">4</button>
        <button class="key" @click="onKey('5')" data-k="5">5</button>
        <button class="key" @click="onKey('6')" data-k="6">6</button>
        <button class="key" @click="onKey('-')" data-k="-">−</button>

        <button class="key" @click="onKey('1')" data-k="1">1</button>
        <button class="key" @click="onKey('2')" data-k="2">2</button>
        <button class="key" @click="onKey('3')" data-k="3">3</button>
        <button class="key" @click="onKey('+')" data-k="+">+</button>

        <button
          class="key"
          style="grid-column: span 2"
          @click="onKey('0')"
          data-k="0">
          0
        </button>
        <button class="key" @click="onKey('.')" data-k=".">.</button>
        <button class="key primary" @click="onKey('=')" data-k="=">=</button>
      </div>
    </GlassCard>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from "vue"

// @ts-ignore - SFC default export by Vue plugin
import GlassCard from "./ui/GlassCard.vue"

const expr = ref<string>("")
const hasError = ref<boolean>(false)
const rootEl = ref<HTMLElement | null>(null)

const displayText = computed(() =>
  hasError.value ? "Error" : expr.value || "0"
)

function onKey(k: string) {
  if (k === "C") {
    expr.value = ""
    hasError.value = false
    return
  }
  if (k === "BS") {
    expr.value = expr.value.slice(0, -1)
    hasError.value = false
    return
  }
  if (k === "=") {
    evaluate()
    return
  }
  // Any normal key input should clear error state and append
  hasError.value = false
  expr.value += k
}

function evaluate() {
  try {
    const sanitized = expr.value.trim()
    if (!sanitized) return

    const value = evaluateExpression(sanitized)
    expr.value = String(value)
    hasError.value = false
  } catch (_e) {
    hasError.value = true
  }
}

// Evaluate simple arithmetic expression with operators + - * / % and decimals
function evaluateExpression(expression: string): number {
  // Tokenize with support for unary minus
  type Token = { type: "num"; value: number } | { type: "op"; value: string }

  const tokens: Token[] = []
  let i = 0
  let expectNumber = true

  while (i < expression.length) {
    const ch = expression[i]
    if (ch === " ") {
      i++
      continue
    }

    if (/[0-9.]/.test(ch) || (expectNumber && ch === "-")) {
      let numStr = ""
      if (expectNumber && ch === "-") {
        numStr += "-"
        i++
      }
      let hasDot = false
      while (i < expression.length) {
        const d = expression[i]
        if (d === ".") {
          if (hasDot) break // 防止出现 1.1.1 这种情况
          hasDot = true
          numStr += d
          i++
          continue
        }
        if (/[0-9]/.test(d)) {
          numStr += d
          i++
          continue
        }
        break
      }
      const num = Number(numStr)
      if (Number.isNaN(num)) throw new Error("Invalid number")
      tokens.push({ type: "num", value: num })
      expectNumber = false
      continue
    }

    if ("+-*/%".includes(ch)) {
      tokens.push({ type: "op", value: ch })
      i++
      expectNumber = true
      continue
    }

    throw new Error("Invalid character")
  }

  // Shunting-yard to RPN
  const precedence = (op: string) => (op === "+" || op === "-" ? 1 : 2) // 加减优先级为1，乘除优先级为2
  const output: Token[] = []
  const ops: string[] = []

  for (const t of tokens) {
    if (t.type === "num") {
      output.push(t)
    } else {
      while (
        ops.length > 0 &&
        precedence(ops[ops.length - 1]) >= precedence(t.value)
      ) {
        output.push({ type: "op", value: ops.pop() as string })
      }
      ops.push(t.value)
    }
  }
  while (ops.length > 0) output.push({ type: "op", value: ops.pop() as string })

  // Evaluate RPN
  const stack: number[] = []
  for (const t of output) {
    if (t.type === "num") {
      stack.push(t.value)
    } else {
      const b = stack.pop()
      const a = stack.pop()
      if (a === undefined || b === undefined) throw new Error("Malformed")
      let r = 0
      switch (t.value) {
        case "+":
          r = safeAdd(a, b)
          break
        case "-":
          r = safeSub(a, b)
          break
        case "*":
          r = safeMul(a, b)
          break
        case "/":
          r = safeDiv(a, b)
          break
        case "%":
          r = safeMod(a, b)
          break
        default:
          throw new Error("Unknown op")
      }
      stack.push(r)
    }
  }
  if (stack.length !== 1) throw new Error("Malformed")
  return stack[0]
}

// Floating-point safe operations with rounding to mitigate precision errors
const DECIMAL_PLACES = 12
function roundTo(value: number, places = DECIMAL_PLACES): number {
  const factor = 10 ** places
  return Math.round((value + Number.EPSILON) * factor) / factor
}
function safeAdd(a: number, b: number): number {
  return roundTo(a + b)
}
function safeSub(a: number, b: number): number {
  return roundTo(a - b)
}
function safeMul(a: number, b: number): number {
  return roundTo(a * b)
}
function safeDiv(a: number, b: number): number {
  return roundTo(a / b)
}
function safeMod(a: number, b: number): number {
  // Align with JS behavior but rounded to reduce artifacts
  return roundTo(a % b)
}

// Keyboard support
function isEditableElement(el: Element | null): boolean {
  if (!el) return false
  const tag = (el as HTMLElement).tagName
  const editable = (el as HTMLElement).isContentEditable
  return (
    editable ||
    tag === "INPUT" ||
    tag === "TEXTAREA" ||
    (tag === "DIV" && (el as HTMLElement).isContentEditable)
  )
}

function handleKeydown(e: KeyboardEvent) {
  // Do not intercept when typing in form fields
  if (isEditableElement(document.activeElement)) return

  const key = e.key
  // Single char: digits/operators/decimal/percent
  if (/^[0-9.+\-*/%]$/.test(key)) {
    onKey(key)
    e.preventDefault()
    return
  }
  // Enter / equals
  if (key === "Enter" || key === "=" || key === "NumpadEnter") {
    onKey("=")
    e.preventDefault()
    return
  }
  // Backspace -> delete last
  if (key === "Backspace") {
    onKey("BS")
    e.preventDefault()
    return
  }
  // Escape or C -> clear
  if (key === "Escape" || key.toLowerCase() === "c") {
    onKey("C")
    e.preventDefault()
    return
  }
}

onMounted(() => {
  window.addEventListener("keydown", handleKeydown, { passive: false })
})

onUnmounted(() => {
  window.removeEventListener("keydown", handleKeydown)
})
</script>

<style scoped>
.display {
  background: #ffffff;
}

.key {
  background: #ffffff;
}
.key.primary {
  background: #1489fe;
  border-color: #1489fe;
}
</style>
